package com.dirox.jira.notes.action;

import com.atlassian.crowd.embedded.api.User;
import com.atlassian.jira.bc.issue.search.SearchService;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.MutableIssue;
import com.atlassian.jira.issue.search.SearchException;
import com.atlassian.jira.jql.builder.JqlQueryBuilder;
import com.atlassian.jira.project.Project;
import com.atlassian.jira.project.ProjectManager;
import com.atlassian.jira.rest.api.util.ErrorCollection;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.jira.security.PermissionManager;
import com.atlassian.jira.security.Permissions;
import com.atlassian.jira.security.groups.GroupManager;
import com.atlassian.jira.security.xsrf.RequiresXsrfCheck;
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.jira.user.util.UserUtil;
import com.atlassian.jira.web.action.JiraWebActionSupport;
import com.atlassian.jira.web.bean.PagerFilter;
import com.atlassian.plugins.rest.common.json.DefaultJaxbJsonMarshaller;
import com.atlassian.plugins.rest.common.json.JaxbJsonMarshaller;
import com.atlassian.query.Query;
import com.dirox.jira.notes.entities.Note;
import com.dirox.jira.notes.service.NoteService;
import com.dirox.jira.notes.utils.ActivityHelper;
import com.dirox.jira.notes.utils.JiraHelper;
import com.dirox.jira.notes.utils.MailHelper;
import static com.google.common.base.Preconditions.checkNotNull;
import org.apache.commons.lang.StringUtils;
import webwork.action.ActionContext;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import java.util.SortedSet;


public class CreateNote extends JiraWebActionSupport {
	private static final long serialVersionUID = 1L;
	public static String APPLICATION_NAME_CREATED_NOTE = "JIRA Created Note";
	public static String APPLICATION_NAME_UPDATED_NOTE = "JIRA Updated Note";
	public static String APPLICATION_NAME_DELETED_NOTE = "JIRA Deleted Note";
	public static String APPLICATION_NAME_TAGGED_ISSUE = "JIRA Tagged Issue Note";
	public static String APPLICATION_NAME_TAGGED_USER = "JIRA Tagged User Note";
	private JiraAuthenticationContext context;
	private NoteService noteService;
	private SearchService searchService;
	private UserManager userManager;
	private UserUtil userUtil;
	private IssueManager issueManager;
	private ProjectManager projectManager;
	private GroupManager groupManager;
	private PermissionManager permissionManager;
	private ErrorCollection errors;
	private Hashtable<String, Object> responseJson;
	private String content;
	private String priority;
	private String userName;
	private String issueKey;
	private ActivityHelper activityHelper;
	private JiraHelper jiraHelper;

	/**
	 * This method return rendered html for create note form
	 */
	public String doDefault() throws Exception {
		return "html";
	}

	/**
	 * This method is execute action to create new note
	 *
	 * @return JSON content
	 */
	@RequiresXsrfCheck
	protected String doExecute() throws Exception {
		// validate the input parameters
		if (!validateInput()) {
			return ERROR;
		}

		ActionContext.getResponse()
		             .setContentType("application/json");

		// add note
		com.dirox.jira.notes.entities.User owner = noteService
			                                                                                .currentUser();
		Note note = noteService.add(content,
		                            Integer.parseInt(priority),
		                            owner);

		// post an activity
		String title = context.getI18nHelper()
		                      .getText("notes.text.user.created.note")
		                      .replaceAll(
			"%user%",
			context.getLoggedInUser().getDisplayName());
		activityHelper.postActivitity(APPLICATION_NAME_CREATED_NOTE, title, "");

		// tagged user
		if (!StringUtils.isBlank(userName)) {
			User user = userManager.getUser(userName);
			noteService.tagNoteToUser(note, user);

			// post an activity
			String title1 = context.getI18nHelper()
			                       .getText("notes.text.user.tagged.user")
			                       .replaceAll(
				"%user1%",
				context.getLoggedInUser().getDisplayName())
			                       .replaceAll(
				"%user2%",
				jiraHelper.getProfileLinkHtml(user));
			activityHelper.postActivitity(
				APPLICATION_NAME_TAGGED_USER,
				title1,
				"");

			// send notification email to tagged user
			String to = user.getEmailAddress();
			String subject = context.getI18nHelper()
			                        .getText("notes.text.mail.subject");
			String content = context.getI18nHelper()
			                        .getText("notes.text.mail.content")
			                        .replaceAll(
				"%user%",
				note.getOwner().getDisplayName());
			MailHelper.send(to, subject, content);
		}

		// tagged issue
		if (!StringUtils.isBlank(issueKey)) {
			MutableIssue issue = issueManager.getIssueObject(issueKey);
			noteService.tagNoteToIssue(note, issue);

			// post an activity
			String title1 = context.getI18nHelper()
			                       .getText("notes.text.user.tagged.issue")
			                       .replaceAll(
				"%user%",
				context.getLoggedInUser().getDisplayName())
			                       .replaceAll(
				"%issue%",
				jiraHelper.getIssueLinkHtml(issue.getKey()));
			activityHelper.postActivitity(
				APPLICATION_NAME_TAGGED_ISSUE,
				title1,
				"");
		}

		this.responseJson = new Hashtable<String, Object>();
		this.responseJson.put("response",
		                      getText("notes.code.success"));
		this.responseJson.put("success", true);

		return "json";
	}

	/**
	 * Validate the input parameters
	 *
	 * @return true if validating input success, else return false
	 */
	private boolean validateInput() {
		// check if content is empty
		if (StringUtils.isBlank(content)) {
			this.errors = ErrorCollection.of(
				getText("notes.error.empty.content"));

			return false;
		}

		// check if priority is an integer number
		if (StringUtils.isBlank(priority) || !isInteger(priority)) {
			this.errors = ErrorCollection.of(
				getText("notes.error.priority.must.be.integer"));

			return false;
		}

		// validation value of priority
		int iPriority = Integer.parseInt(priority);

		if ((iPriority != Note.PRIORITY_MUST)
			    && (iPriority != Note.PRIORITY_COULD)) {
			this.errors = ErrorCollection.of(
				getText("notes.error.invalid.priority"));

			return false;
		}

		// validation user
		if (!StringUtils.isBlank(userName) && !userName.equals("-1")) {
			com.atlassian.crowd.embedded.api.User user = userManager.getUser(
				userName);

			// check if user is not existed
			if (user == null) {
				this.errors = ErrorCollection.of(
					getText("notes.error.not.exist.user"));

				return false;
			}

			// check if user is equal to current user
			if (user.equals(getLoggedInUser())) {
				this.errors = ErrorCollection.of(
					getText("notes.error.current.user"));

				return false;
			}

			// check if no permission on the user
			Collection<User> users = userUtil.getAllUsersInGroups(
				groupManager.getAllGroups());

			if (!users.contains(user)) {
				this.errors = ErrorCollection.of(
					getText("notes.error.invalid.user"));

				return false;
			}
		}

		// validation issue
		if (!StringUtils.isBlank(issueKey) && !issueKey.equals("-1")) {
			MutableIssue issue = issueManager.getIssueObject(issueKey);

			// check if issue is not existed
			if (issue == null) {
				this.errors = ErrorCollection.of(
					getText("notes.error.not.exist.issue"));

				return false;
			}

			// check if no permission on the issue
			if (!permissionManager.hasPermission(
					Permissions.BROWSE,
					issue,
					getLoggedInUser())) {
				this.errors = ErrorCollection.of(
					getText("notes.error.no.browse.permission.issue"));

				return false;
			}
		}

		return true;
	}

	private boolean isInteger(String s) {
		try {
			Integer.parseInt(s);
		} catch (NumberFormatException e) {
			return false;
		}

		return true;
	}

	public List<Issue> getIssues() {
		Query query = JqlQueryBuilder.newClauseBuilder()
		                             .buildQuery();

		try {
			List<Issue> issues = searchService.search(
				context.getLoggedInUser(),
				query,
				PagerFilter.getUnlimitedFilter())
			                                  .getIssues();

			for (Issue issue : issues) {
				if (!permissionManager.hasPermission(
						Permissions.BROWSE,
						issue,
						context.getLoggedInUser())) {
					issues.remove(issue);
				}
			}

			return issues;
		} catch (SearchException e) {
			e.printStackTrace();

			return null;
		}
	}

	public List<Project> getProjects() {
		return projectManager.getProjectObjects();
	}

	public SortedSet<User> getUsers() {
		return userUtil.getAllUsersInGroups(groupManager.getAllGroups());
	}

	public String getResponseJson() {
		final JaxbJsonMarshaller marshaller = new DefaultJaxbJsonMarshaller();

		return marshaller.marshal(this.responseJson);
	}

	public String getErrorJson() {
		final JaxbJsonMarshaller marshaller = new DefaultJaxbJsonMarshaller();
		this.responseJson = new Hashtable<String, Object>();
		this.responseJson.put("response", errors);
		this.responseJson.put("success", false);

		return marshaller.marshal(this.responseJson);
	}

	public void setContent(String content) {
		try {
			this.content = java.net.URLDecoder.decode(content, "UTF-8");
		} catch (Exception e) {
			this.content = content;
		}
	}

	public void setPriority(String priority) {
		this.priority = priority;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public void setIssueKey(String issueKey) {
		this.issueKey = issueKey;
	}

	public void setNoteService(NoteService noteService) {
		this.noteService = checkNotNull(noteService);
	}

	public void setUserManager(UserManager userManager) {
		this.userManager = checkNotNull(userManager);
	}

	public void setProjectManager(ProjectManager projectManager) {
		this.projectManager = checkNotNull(projectManager);
	}

	public void setIssueManager(IssueManager issueManager) {
		this.issueManager = checkNotNull(issueManager);
	}

	public void setPermissionManager(PermissionManager permissionManager) {
		this.permissionManager = checkNotNull(permissionManager);
	}

	public void setSearchService(SearchService searchService) {
		this.searchService = checkNotNull(searchService);
	}

	public void setUserUtil(UserUtil userUtil) {
		this.userUtil = checkNotNull(userUtil);
	}

	public void setGroupManager(GroupManager groupManager) {
		this.groupManager = checkNotNull(groupManager);
	}

	public void setActivityHelper(ActivityHelper activityHelper) {
		this.activityHelper = activityHelper;
	}

	public void setJiraHelper(JiraHelper jiraHelper) {
		this.jiraHelper = jiraHelper;
	}

	public void setContext(JiraAuthenticationContext context) {
		this.context = context;
	}
}
